//
//  MFAnalytics.m
//  NewMissFresh
//
//  Created by 董宝君 on 2019/6/10.
//  Copyright © 2019 com.missfresh. All rights reserved.
//

#import "MFAnalytics.h"
#import "MFALogger.h"
#import "MFAMessageQueue.h"
#import "MFAReachability.h"
#import "MFANetwork.h"
#import "MFAJSONUtility.h"
#import "MFAExceptionReporter.h"
#import "MFACommonUtility.h"

/**
 同步调用主线程方法
 
 @param block 执行的方法
 */
void MFAPerformBlockSyncOnMainThread(void (^ _Nonnull block)(void)) {
    if (!block) return;
    
    if ([NSThread isMainThread]) {
        block();
    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            block();
        });
    }
}

static MFAnalytics *sharedAnalytics = nil;

@interface MFAnalytics ()
@property (nonatomic, strong, readwrite) MFAConfigOptions *config;
@property (nonatomic, assign, readwrite) MFAnalyticsNetworkType networkPolicy;
@property (nonatomic, strong) MFANetwork *network;

@property (nonatomic, strong) dispatch_queue_t serialQueue;
@property (nonatomic, strong) MFAMessageQueue *messageQueue;
@property (nonatomic, strong) NSTimer *timer;

//是否是被动启动
@property (nonatomic, assign) BOOL launchedPassively;
//进入非活动状态，比如双击 home、系统授权弹框
@property (nonatomic, assign) BOOL applicationWillResignActive;
//进入后台是否执行一次上报 默认YES暂且不提供给外部使用
@property (nonatomic, assign) BOOL flushBeforeEnterBackground;
@end

@implementation MFAnalytics

#pragma mark - init

+ (MFAnalytics *)sharedAnalyticsWithConfig:(MFAConfigOptions *)config {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedAnalytics = [[MFAnalytics alloc] initWithConfig:config];
    });
    return sharedAnalytics;
}

+ (MFAnalytics * _Nullable)sharedAnalytics {
    if(sharedAnalytics.config.disableSDK){
        return nil;
    }
    return sharedAnalytics;
}

- (instancetype)initWithConfig:(MFAConfigOptions *)config {
    if(self = [super init]){
        _config = config;
        
        //默认 3G、4G、WIFI环境上报数据
        _networkPolicy = MFAnalyticsNetworkType3G | MFAnalyticsNetworkType4G | MFAnalyticsNetworkTypeWIFI;
        //串行队列
        _serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"com.missfresh.analytics.serialQueue.%p",self] UTF8String], DISPATCH_QUEUE_SERIAL);
        _applicationWillResignActive = NO;
        _flushBeforeEnterBackground = YES;
        //通过sqlite创建的消息队列v1版本
        _messageQueue = [[MFAMessageQueue alloc] initWithFilePath:[MFACommonUtility filePathWithName:@"message-v1"]];
        if(!_messageQueue){
            MFADebug(@"SqliteException: init Message Queue in Sqlite fail");
            [[MFAExceptionReporter sharedReporter] reportMessage:@"SqliteException: init Message Queue in Sqlite fail" pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
        }
        
        //被动启动判断
        MFAPerformBlockSyncOnMainThread(^{
            UIApplicationState applicationState = UIApplication.sharedApplication.applicationState;
            if (applicationState == UIApplicationStateBackground) {
                self->_launchedPassively = YES;
            }
        });
        
        //网络
        _network = [[MFANetwork alloc] initWithServerURL:[NSURL URLWithString:_config.serverURL]];
        
        //非被动启动，启动定时器
        if(!_launchedPassively){
            [self startFlushTimer];
        }
        
        //设置监听者
        [self setupListeners];
    }
    return self;
}

#pragma mark - public

- (void)setFlushNetworkPolicy:(MFAnalyticsNetworkType)networkPolicy {
    _networkPolicy = networkPolicy;
}

- (void)enableLog:(BOOL)enabelLog {
    [MFALogger enableLog:enabelLog];
}

- (void)enableEncrypted:(BOOL)encrypted {
    [MFAnalytics sharedAnalytics].config.encrypted = encrypted;
}

- (void)track:(nonnull NSDictionary *)event {
    [self track:event realtime:NO];
}

- (void)track:(nonnull NSDictionary *)event realtime:(BOOL)realtime {
    //sdk关闭 不进行处理
    if(self.config.disableSDK){
        return;
    }
    
    //没有事件，不进行处理
    if(!event){
        return;
    }
    
    //不是合法的JSON对象格式，暂不处理
    if(![NSJSONSerialization isValidJSONObject:event]){
        return;
    }
    
    dispatch_async(self.serialQueue, ^{
        
        //把外部传入的对象编码转换成json字符串
        NSString *eventString = [MFAJSONUtility convertObjectToJSONString:event];
        
        if(!eventString.length){
            return;
        }
        
        //入队消息
        [self enqueueMessage:eventString];
        
        //如果配置了实时触发或者达到了规定的消息条数后触发上报
        if(realtime || [self.messageQueue messageCount] >= self.config.flushBulkSize){
            [self flush];
        }
    });
}

- (void)deleteAll {
    [self.messageQueue deleteAllMessage];
}

#pragma mark - private

- (void)enqueueMessage:(NSString *)message {
    [self.messageQueue addMessage:message type:@"POST"];
}

/**
 启动定时器
 */
- (void)startFlushTimer {
    if (self.config.disableSDK || (self.timer && [self.timer isValid])) {
        return;
    }
    MFADebug(@"starting flush timer.");
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.config.flushInterval > 0) {
            double interval = self.config.flushInterval > 100 ? (double)self.config.flushInterval / 1000.0 : 0.1f;
            self.timer = [NSTimer scheduledTimerWithTimeInterval:interval
                                                          target:self
                                                        selector:@selector(flush)
                                                        userInfo:nil
                                                         repeats:YES];
            [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
        }
    });
}


/**
 停止定时器
 */
- (void)stopFlushTimer {
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.timer) {
            [self.timer invalidate];
        }
        self.timer = nil;
    });
}

/**
 上报
 */
- (void)flush {
    dispatch_async(self.serialQueue, ^{
        [self flushVacuumWhenAfterFlushing:NO];
    });
}

/**
 上报数据，在上报完成后vacuum SQLite数据库

 @param vacuumAfterFlushing 上报完成后是否优化整理数据库存储空间
 */
- (void)flushVacuumWhenAfterFlushing:(BOOL)vacuumAfterFlushing {
    //判断上报副武器的地址
    if(!self.network.serverURL){
        [[MFAExceptionReporter sharedReporter] reportMessage:@"network lose server url" pageName:MF_ANALYTICS_ERROR_MODULE_NETWORK];
        return;
    }
    //判断允许上报的网络环境
    if(!([MFACommonUtility networkType] & self.networkPolicy)){
        [[MFAExceptionReporter sharedReporter] reportMessage:@"network environment mismatching" pageName:MF_ANALYTICS_ERROR_MODULE_NETWORK];
        return;
    }
    
    //上报数据
    [self flushWithType:@"POST" flushSize:self.config.transLimit];
    
    //上报完成后是否优化数据库存储
    if(vacuumAfterFlushing){
        if(![self.messageQueue vacuum]){
            MFAError(@"failed to VACUUM SQLite.");
            [[MFAExceptionReporter sharedReporter] reportMessage:@"failed to VACUUM SQLite." pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
        }
    }
    
    MFADebug(@"events flushed.");
}

/**
 根据类型和条数上报数据 （类型目前只有一种POST，保留字段后续会使用）

 @param type 消息类型
 @param flushSize 上报的数据大小
 */
- (void)flushWithType:(NSString *)type flushSize:(NSInteger)flushSize {
    //从消息队列头取出一定条数的消息
    NSArray <NSString *> *messageList = [self.messageQueue loadMessageFromHeadWithMessageSize:flushSize type:type];
    if(!messageList){
        MFAError(@"Failed to get records from SQLite.");
        [[MFAExceptionReporter sharedReporter] reportMessage:@"Failed to get records from SQLite." pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
        return;
    }
    
    //上传获取到的记录。如果数据上传完成，结束递归
    if(messageList.count == 0 || ![self.network flushEvents:messageList]){
        return;
    }
    
    //删除已上传的记录。删除失败，结束递归
    if(![self.messageQueue removeMessageFromHeadWithMessageSize:messageList.count type:type]){
        MFAError(@"Failed to remove records from SQLite.");
        [[MFAExceptionReporter sharedReporter] reportMessage:@"Failed to remove records from SQLite." pageName:MF_ANALYTICS_ERROR_MODULE_SQLITE];
        return;
    }
    
    //继续上传剩余数据
    [self flushWithType:type flushSize:flushSize];
}

#pragma mark - UIApplication Events

- (void)setupListeners {
    // 监听 App 启动或结束事件
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    
    [notificationCenter addObserver:self
                           selector:@selector(applicationWillEnterForeground:)
                               name:UIApplicationWillEnterForegroundNotification
                             object:nil];
    
    [notificationCenter addObserver:self
                           selector:@selector(applicationDidBecomeActive:)
                               name:UIApplicationDidBecomeActiveNotification
                             object:nil];
    
    [notificationCenter addObserver:self
                           selector:@selector(applicationWillResignActive:)
                               name:UIApplicationWillResignActiveNotification
                             object:nil];
    
    [notificationCenter addObserver:self
                           selector:@selector(applicationDidEnterBackground:)
                               name:UIApplicationDidEnterBackgroundNotification
                             object:nil];
}

- (void)applicationWillEnterForeground:(NSNotification *)notification {
    MFADebug(@"%@ application will enter foreground", self);
    
    self.launchedPassively = NO;
}

- (void)applicationDidBecomeActive:(NSNotification *)notification {
    MFADebug(@"%@ application did become active", self);
    
    if (self.config.disableSDK) {
        //停止 SDK 的 flushtimer
        [self stopFlushTimer];
        [self flush];//停止采集数据之后 flush 本地数据
    }
    
    if (self.applicationWillResignActive) {
        self.applicationWillResignActive = NO;
    }else{
        [self startFlushTimer];
    }
}

- (void)applicationWillResignActive:(NSNotification *)notification {
    MFADebug(@"%@ application will resign active", self);
    self.applicationWillResignActive = YES;
}

- (void)applicationDidEnterBackground:(NSNotification *)notification {
    MFADebug(@"%@ application did enter background", self);
    self.applicationWillResignActive = NO;
    
    [self stopFlushTimer];
    
    self.launchedPassively = NO;
    
    UIApplication *application = UIApplication.sharedApplication;
    __block UIBackgroundTaskIdentifier backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    // 结束后台任务
    void (^endBackgroundTask)(void) = ^() {
        [application endBackgroundTask:backgroundTaskIdentifier];
        backgroundTaskIdentifier = UIBackgroundTaskInvalid;
    };
    
    backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
        endBackgroundTask();
    }];
    
    if (self.flushBeforeEnterBackground) {
        dispatch_async(self.serialQueue, ^{
            [self flushVacuumWhenAfterFlushing:YES];
            endBackgroundTask();
        });
    } else {
        dispatch_async(self.serialQueue, ^{
            endBackgroundTask();
        });
    }
}

@end
